#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <cstdlib>
#include <fstream>
#include <ctime>
#include <cmath>

using namespace std;

//programming part 1=======================================================================

class trainStopData{
	string stop_id, stop_name;
	double stop_lat, stop_lon;
	public:
	trainStopData(const string& rawData){
		istringstream input(rawData);
		string token;
		getline(input,token,',');
		stop_id = token;
		getline(input,token,',');//junk i dont need
		getline(input,token,',');
		stop_name = token;
		getline(input,token,',');//junk i don't need
		getline(input,token,',');
		stop_lat = atof(token.c_str());
		getline(input,token,',');
		stop_lon = atof(token.c_str());
	}
	friend ostream& operator<<(ostream& os, const trainStopData& tsd);

	string get_id()const{
		return stop_id;
	}
	string get_stop_name()const{
		return stop_name;
	}
	double get_latitude()const{
		return stop_lat;
	}
	double get_longitude()const{
		return stop_lon;
	}
};

ostream& operator<<(ostream& os, const trainStopData& tsd){
	os << tsd.stop_id << " " << tsd.stop_name << " " << tsd.stop_lat << " " << tsd.stop_lon;
	return os;
}

//silly haversine BS
double degrad(double d) {
   return d * M_PI / 180;
}
double haverdist(double lat1, double longit1, double lat2, double longit2)
{
     double r = 6371;
     double dlat = degrad(lat2 - lat1);
     double dlongit = degrad(longit2 - longit1);
     double a = sin(dlat/2)*sin(dlat/2)+
        cos(degrad(lat1))*cos(degrad(lat2))*sin(dlongit/2)*sin(dlongit/2);
     double c = 2*atan2(sqrt(a),sqrt(1-a));
     return r*c;
}


//templated perform_if
#define vItr vector<trainStopData>::iterator	//just easier to name it
template<class func1, class func2>
int perform_if(vItr itrStart, vItr itrEnd, func1 pred, func2 op){
	int count = 0;
	vItr current = itrStart;
	while(current != itrEnd){
		if(pred(*current)){
			op(*current);
			count++;
		}
		current++;
	}
	return count;
	//loop for every element
	//check against pred
	//call op
	//increment
	//return count
}

class isSubwayStop{
public:
	isSubwayStop(string  sid) : stopId(sid){}
	bool operator()(const trainStopData& stop){
		return (stop.get_id()  == stopId);
	}

	string stopId;
};

class isStopOnRoute{
public:
	isStopOnRoute(char rt) : route(rt) {}
	bool operator()(const trainStopData& stop){
		char stopRoute = stop.get_id().c_str()[0];
		return route == stopRoute;
	}
	char route;
};

class printTrainStopInfo{
public:
	void operator()(const trainStopData& tsd){
		cout << tsd << endl; //operator defined above
	}	
};

class isSubwayStopNearX{
public:
	isSubwayStopNearX(double lat, double lon, double d) : lat(lat), lon(lon), d(d) {}
	bool operator()(const trainStopData& stop){
		double dist = haverdist(lat,lon,stop.get_latitude(),stop.get_longitude());
		return dist < d;
	}
	double lat, lon, d;
};

int main(){
	//Problem 1
	ifstream ifs("MTA_train_stop_data.txt");
	if(!ifs){
		cerr << "No file :(" << endl;
		return -1;
	}

	vector<trainStopData> stops;

	string currentData;
	while(getline(ifs, currentData)){
		stops.push_back(trainStopData(currentData));
	}

	cout << "File loaded." << endl;

	while(true){
		//print instructins
		//check the input
		//do something
		cout << "Options: " << endl;
		cout << "a)Find all stops on a line.\nb)Find info on a specific stop\nc)Find Stops near specific coordinates\nd)quit" <<endl;
		cout << "Enter the letter of the option you would like: ";
		char inp;
		cin >> inp;
		if(inp == 'a'){
			char route;
			cout << "Enter Route # or Leter: ";
			cin >> route;
			if(perform_if(stops.begin(), stops.end(), isStopOnRoute(route), printTrainStopInfo()) == 0){
				cout << "No Route Found." << endl << endl;
			}
			cout << endl;
		}
		else if(inp == 'b'){
			string searchID;
			cout << "Enter Stop ID: ";
			cin >> searchID;
			if( perform_if(stops.begin(), stops.end(), isSubwayStop(searchID), printTrainStopInfo()) == 0){
				cout << "No Stop Found." << endl << endl;
			}
			cout << endl;
		}
		else if(inp == 'c'){
			double ulat, ulon, ud;
			cout << "Enter your latitude, longitude, and how close you want the stop to be: " << endl;
			cin >> ulat >> ulon >> ud;
			if( perform_if(stops.begin(), stops.end(), isSubwayStopNearX(ulat,ulon,ud), printTrainStopInfo()) == 0 ){
				cout << "No stops found" << endl << endl;
			}
			cout << endl;
		}
		else if(inp == 'd'){
			break;
		}
	}	

}
